﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Linq;
using System.IO;

namespace LdsDownloader
{

    enum bookType
    {
        Liahona,
        Ensign,
        Scriptures,
        Friend,
        NewEra,
        Other
    };

    static class XmlParser
    {
        private static string path;
        private static int order = 1;
        private static XmlDocument opf;
        private static XmlDocument ncx;
        public static ShowExceptionBox showExcept;
        public static showStatus showProgress;
        private static List<string> urlList = new List<string>();
        private static bookType urlType;
        private static string langCode;
        private static string bookTitle;
        private static string bookAuthor = "The Church of Jesus Christ of Latter-day Saints";

        public static string Path{ 
            get{
                return path;
            } 
            set{
                if (!string.IsNullOrEmpty(value) && path != value)
                {
                    path = value;
                    if(!path.EndsWith("\\")){
                        path += "\\";
                    }
                }
            } 
        }

        private static void parseURL(string url)
        {
            defineLang(url);
            defineURLType(url);
        }

        private static void defineLang(string url)
        {
            int startIndex = url.IndexOf("lang=");
            if (startIndex >= 0)
            {
                string langString = url.Substring(startIndex + 5, 3);
                if (langString.Equals("por")) langCode = "pt";
                else if (langString.Equals("spa")) langCode = "es";
                else if (langString.Equals("deu")) langCode = "de";
                else if (langString.Equals("fra")) langCode = "fr";
                else if (langString.Equals("ita")) langCode = "it";
                else if (langString.Equals("eng")) langCode = "en";
            }
            else
            {
                langCode = "en";
            }
        }


        private static void defineURLType(string url)
        {
            if (url.Contains("/scriptures/")) urlType = bookType.Scriptures;
            else if (url.Contains("/liahona/")) urlType = bookType.Liahona;
            else if (url.Contains("/ensign/")) urlType = bookType.Ensign;
            else if (url.Contains("/new-era/")) urlType = bookType.NewEra;
            else if (url.Contains("/friend/")) urlType = bookType.Friend;
            else urlType = bookType.Other;

        }

        public static void processPage(string url)
        {
            parseURL(url);
            urlList.Add(url);
            for (int i = 0; i < urlList.Count; ++i)
            {
                showProgress(urlList[i], i, urlList.Count);
                readXML(urlList[i],i);
            }
            showProgress("Done!", urlList.Count, urlList.Count);
        }

        public static void readXML(string url, int order)
        {
            //string url = "http://lds.org/scriptures/bofm?lang=por";
            //StringBuilder result = new StringBuilder();
            try
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ProhibitDtd = false;
                using (XmlReader reader = XmlReader.Create(url,settings))
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(reader);
                    XmlNode title = doc.SelectSingleNode("//head/title");
                    XmlNode primary = doc.SelectSingleNode("//div[@id='primary']");
                    
                    //Will get the bookTitle from the first document
                    if (order == 0)
                    {
                        bookTitle = title.InnerText;
                        if (urlType != bookType.Scriptures)
                        {
                            bookTitle.Remove(bookTitle.IndexOf("Table"));                            
                        }
                        bookTitle.Trim();
                    }

                    XmlNodeList urls;
                    if (urlType == bookType.Scriptures)
                    {
                        urls = doc.SelectNodes("//div[@id='primary']//li/a");
                    }
                    else
                    {
                        urls = doc.SelectNodes("//div[@id='primary']//td/a");
                    }

                    foreach (XmlNode node in urls)
                    {
                        if (node.ParentNode.Attributes["class"] == null)
                        {
                            if(node.Attributes["href"].Value != null)
                            {
                                urlList.Add(node.Attributes["href"].Value);
                            }
                        }
                        //else if (node.ParentNode.Attributes["class"].Value == "prev" ||
                        //    node.ParentNode.Attributes["class"].Value == "next")
                        //{
                        //    break;
                        //}
                    }

                    //XmlNamespaceManager nsmgr = new XmlNamespaceManager(ncx.NameTable);
                    //nsmgr.AddNamespace("ncx", ncx.DocumentElement.NamespaceURI);
                    //XmlNode text = ncx.SelectSingleNode("//ncx:docTitle/ncx:text", nsmgr);
                    //text.FirstChild.Value = bookTitle;

                    //XPathDocument doc = new XPathDocument(reader);
                    //XPathNavigator navi = doc.CreateNavigator();
                    //XPathExpression expr = navi.Compile("//div[@id='primary']");
                    //XPathNodeIterator iter = navi.Select(expr);
                    //while (iter.MoveNext())
                    //{
                    //    result.AppendLine(iter.Current.OuterXml);
                    //}
                    
                    //expr = navi.Compile("//head/title");
                    //iter = navi.Select(expr);
                    //string pagetitle = iter.Current.InnerXml;
                    writeXML(title.InnerXml, primary.OuterXml);                    
                }
            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }
        }

        //public static void readXML(string url, out string info)
        //{
        //    StringBuilder result = new StringBuilder();
        //    try
        //    {
        //        XmlReaderSettings settings = new XmlReaderSettings();
        //        settings.ProhibitDtd = false;
        //        using (XmlReader reader = XmlReader.Create(url, settings))
        //        {
        //            XmlDocument doc = new XmlDocument();
        //            doc.Load(reader);
        //            XmlNode title = doc.SelectSingleNode("*");

        //        }
        //    }
        //    catch (XmlException e)
        //    {
        //        showExcept(e.Message, e.StackTrace);
        //    }
        //    info = result.ToString();
        //}

        public static void createContainer()
        {
            createPathIfNotExists(path+ "/META-INF");
            try
            {
                XmlDocument container = new XmlDocument();
                container.LoadXml("<?xml version=\"1.0\"?>" +
                                    "<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">" +
                                    "<rootfiles>" +
                                    "<rootfile full-path=\"content.opf\" media-type=\"application/oebps-package+xml\"/>" +
                                    "</rootfiles>" +
                                    "</container>");
                container.Save(path + "/META-INF/container.xml");
            }
            catch (XmlException e)
            {
                showExcept(e.Message, e.StackTrace);
            }
        }
        
        public static void createTOC(string bookTitle) {
            createPathIfNotExists(path);
            StringBuilder result = new StringBuilder();
            try
            {
                ncx = new XmlDocument();
                ncx.LoadXml("<?xml version='1.0' encoding='utf-8'?>" +
                    "<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\" xml:lang=\""+ (string.IsNullOrEmpty(langCode)? "en": langCode) +"\">" +
                            "<head>" +
                            "<meta content=\"a335de4c-560f-4b75-bcba-1418b726f92c\" name=\"dtb:uid\"/>" +
                            "<meta content=\"2\" name=\"dtb:depth\"/>" +
                            "<meta content=\"Lds Downloader (0.1)\" name=\"dtb:generator\"/>" +
                            "<meta content=\"0\" name=\"dtb:totalPageCount\"/>" +
                            "<meta content=\"0\" name=\"dtb:maxPageNumber\"/>" +
                            "</head>" +
                            "<docTitle>" +
                            "<text>" + (string.IsNullOrEmpty(bookTitle) ? "Title" : bookTitle) + "</text>" +
                            "</docTitle>" +
                            "<docAuthor>" +
                            "<text>"+bookAuthor+"</text>" +
                            "</docAuthor>" +
                            "<navMap />" +
                            "</ncx>");
                //XmlNamespaceManager nsmgr = new XmlNamespaceManager(ncx.NameTable);
                //nsmgr.AddNamespace("ncx", ncx.DocumentElement.NamespaceURI);
                //XmlNode text = ncx.SelectSingleNode("//ncx:docTitle/ncx:text", nsmgr);
                //text.FirstChild.Value = bookTitle;
                //text = ncx.SelectSingleNode("//ncx:docAuthor/ncx:text", nsmgr);
                //text.FirstChild.Value = "This book's author";
            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }
        }

        public static void writeToTOC(){
            try{
                opf.Save(path + "content.opf");
                ncx.Save(path + "toc.xml");
            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }

        
        }

        public static void createOPF()
        {
            createPathIfNotExists(path);
            try
            {
                opf = new XmlDocument();
                opf.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" +
                                "<package version=\"2.0\" unique-identifier=\"uuid_id\" xmlns=\"http://www.idpf.org/2007/opf\">" +
                                "<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:opf=\"http://www.idpf.org/2007/opf\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dcterms=\"http://purl.org/dc/terms/\">" +
                                "<dc:title>" + (string.IsNullOrEmpty(bookTitle) ? "Title" : bookTitle) + "</dc:title>" +
                                "<dc:language>"+(string.IsNullOrEmpty(langCode)? "en" : langCode)+"</dc:language>" +
                                "<dc:identifier id=\"uuid_id\" opf:scheme=\"uuid\">a335de4c-560f-4b75-bcba-1418b726f92c</dc:identifier>" +
                                "<dc:creator opf:role=\"aut\">" + bookAuthor + "</dc:creator>" +
                                "<dc:date>"+DateTimeOffset.Now.ToString("o")+"</dc:date>" +                                
                                "<meta name=\"cover\" content=\"book-cover\"/>" +
                                "</metadata>" +
                                "<manifest />" +
                                "<spine toc=\"ncx\" />" +
                                "<guide />" +
                                "</package>");
                XmlElement manifest = (XmlElement) opf.GetElementsByTagName("manifest")[0];
                XmlElement item = opf.CreateElement("item", manifest.NamespaceURI);
                item.SetAttribute("id", "cover");
                item.SetAttribute("href", "cover.xml");
                item.SetAttribute("media-type", "application/xhtml+xml");
                manifest.AppendChild(item);
            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }
        }

        public static void writeXML(string title, string body) {
            createPathIfNotExists(path);
            try
            {
                XmlDocument newPage = new XmlDocument();
                newPage.LoadXml("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                                "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"" + (string.IsNullOrEmpty(langCode) ? "en" : langCode) + "\">" +
                                "<head>" +
                                "<title />" +
                                "<link rel=\"stylesheet\" href=\"stylesheet.css\" type=\"text/css\" />" +
                                "<meta http-equiv=\"Content-Type\" content=\"application/xhtml+xml; charset=utf-8\" />" +
                                "</head>" +
                                "<body />" +
                                "</html>");
                XmlNamespaceManager nsmgr = new XmlNamespaceManager(newPage.NameTable);
                nsmgr.AddNamespace("tx", newPage.DocumentElement.NamespaceURI);
                XmlNode element = newPage.SelectSingleNode("//tx:head/tx:title", nsmgr);
                element.InnerXml = title;
                element = newPage.SelectSingleNode("//tx:html/tx:body", nsmgr);
                element.InnerXml = body;
                
                newPage.Save(path + getNextFilename());
            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }
        }

        private static string getNextFilename()
        {
            string pageNumber = order.ToString();
            order++;
            StringBuilder filename = new StringBuilder("index_split_00000.xhtml");
            filename.Remove(12, pageNumber.Length);
            filename.Insert(17 - pageNumber.Length, pageNumber);
            return filename.ToString();
        }

        public static void addToManifest() { }
        public static void createMimetype() {
            try
            {
                if (!File.Exists(path + "mimetype"))
                {
                    createPathIfNotExists(path);
                    StreamWriter mimetype = File.CreateText(path + "mimetype");
                    mimetype.Write("application/epub+zip");
                    mimetype.Flush();
                    mimetype.Close();
                }

            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }
        }

        private static void createPathIfNotExists(string createPath)
        {
            try
            {
                if (!Directory.Exists(createPath))
                {
                    Directory.CreateDirectory(createPath);
                }
            }
            catch (Exception e)
            {
                showExcept(e.Message, e.StackTrace);
            }

        }

         
    
    }

}
